package org.jproxy.json;

import java.lang.reflect.Array;
import java.lang.reflect.Method;

import org.jproxy.json.JSONObject.Type;


public class JSONBuilder
{
	private final Object[] args;

	public JSONBuilder()
	{
		args = new Object[]{};
	}

	public String toJSON(Object obj, Class<?> returnType, Type type)
	{
		System.out.println("building object...");
		if(obj == null)
			return "null";
		switch(type)
		{
			case VOID:
				return "undefined";
			case BOOLEAN:
			case NUMBER:
				return String.valueOf(obj);
			case STRING:
				return new StringBuilder("'").append((String) obj).append("'").toString();
			case ARRAY:
				return buildArray(obj, returnType, type);
			case OBJECT:
				return buildObject(obj, returnType, type);
			default:
				return null;
		}
	}

	private String buildArray(Object obj, Class<?> returnType, Type type)
	{
		StringBuilder content = new StringBuilder("[");
		int i = 0;
		Class<?> componentClass = returnType.getComponentType();
		Type componentType = extractJSONType(componentClass);
		while(true)
			try
			{
				content.append(toJSON(Array.get(obj, i++), componentClass, componentType)).append(", ");
			}catch(ArrayIndexOutOfBoundsException e)
			{
				break;
			}
		int size = content.length();
		return content.replace(size - 2, size, "]").toString();
	}


	private String buildObject(Object obj, Class<?> returnType, Type type)
	{
		StringBuilder content = new StringBuilder("{");
		for(Method method : returnType.getDeclaredMethods())
		{
			String name = method.getName();
			if(method.getParameterTypes().length > 0)
				continue;
			if(name.startsWith("get"))
				content.append(extractPropertyName(method)).append(":").append(toJSONAdapter(extractGetterValue(method, obj))).append(", ");
		}
		int size = content.length();
		return content.replace(size - 2, size, "}").toString();
	}

	private String toJSONAdapter(Object obj)
	{
		Class<?> clazz = obj.getClass();
		return toJSON(obj, clazz, extractJSONType(clazz));
	}

	private String extractPropertyName(Method method)
	{
		StringBuilder name = new StringBuilder(method.getName());
		name.delete(0, 3);
		name.replace(0, 1, name.substring(0, 1).toLowerCase());
		return name.toString();
	}

	private Object extractGetterValue(Method method, Object obj)
	{
		try
		{
			return method.invoke(obj, args);
		}catch(Throwable t)
		{
			//TODO ROd -> tratar catch...
			return null;
		}
	}

	private Type extractJSONType(Class<?> clazz)
	{
		Type type = Type.VOID;
		if(String.class.equals(clazz))
			type = Type.STRING;
		else if(isNumber(clazz))
			type = Type.NUMBER;
		else if(Boolean.class.equals(clazz) || boolean.class.equals(clazz))
			type = Type.BOOLEAN;
		else if(clazz.isArray())
			type = Type.ARRAY;
		else if(!clazz.isPrimitive())
			type = Type.OBJECT;
		return type;
	}

	private boolean isNumber(Class<?> clazz)
	{
		return int.class.equals(clazz) || Integer.class.equals(clazz) || long.class.equals(clazz) || Long.class.equals(clazz) || float.class.equals(clazz) || Float.class.equals(clazz) || double.class.equals(clazz) || Double.class.equals(clazz);
	}

}
